import { Component, OnInit, Input } from '@angular/core';
import { Map } from 'ol';
import View from 'ol/View';
import { Image as ImageLayer, Tile as TileLayer } from 'ol/layer';
import { fromLonLat } from 'ol/proj';
import BingMaps from 'ol/source/BingMaps';
import RasterSource from 'ol/source/Raster';
import * as _ from 'lodash';
import { unByKey } from 'ol/Observable';
import { UtilComponent } from '../../interface/util-component.interface';

@Component({
  selector: 'kylin-region-growing',
  templateUrl: './region-growing.component.html',
  styleUrls: ['./region-growing.component.css']
})
export class RegionGrowingComponent implements OnInit, UtilComponent {

  // ---------- 接口实现 begin -------------- //
  hasContent = true;
  map: Map;

  @Input() set data(val) {
    Object.assign(this, val);
  };

  // ---------- 接口实现 end ---------------- //{

  private currentLayers = null;
  private coordinate: any;
  private currentEvent = null;

  constructor() { }

  ngOnInit() {
  }

  add() {
    this.currentLayers = {};
    this.currentEvent = {};
    this.currentLayers.imagery = new TileLayer({
      source: new BingMaps({ key: 'AslSLNN27EriTtgRbJTJHZYZhNEhIQygwI76cYNMuyiXI8vTd9apf7PFFWePMAP9', imagerySet: 'Aerial' })
    });

    const raster = new RasterSource({
      sources: [this.currentLayers.imagery.getSource()],
      operationType: 'image',
      operation: this.growRegion,
      // Functions in the `lib` object will be available to the operation run in
      // the web worker.
      lib: {
        next4Edges: this.next4Edges
      }
    });

    this.currentLayers.rasterImage = new ImageLayer({
      opacity: 0.7,
      source: raster
    });
    this.map.addLayer(this.currentLayers.imagery);
    this.map.addLayer(this.currentLayers.rasterImage);
    this.map.getView().animate({ center: [-119.07, 47.65] }, { zoom: 11 });


    this.currentEvent.click = this.map.on('click', (event) => {
      this.coordinate = event.coordinate;
      raster.changed();
    });

    raster.on('beforeoperations', (event) => {
      const thresholdControl = document.getElementById('threshold');
      // the event.data object will be passed to operations
      const data = event.data;
      data.delta = thresholdControl['value'];
      if (this.coordinate) {
        data.pixel = this.map.getPixelFromCoordinate(this.coordinate);
      }
    });

    function updateControlValue() {
      const thresholdControl = document.getElementById('threshold');
      document.getElementById('threshold-value').innerText = thresholdControl['value'];
    }
    updateControlValue();
    const thresholdControl = document.getElementById('threshold');
    thresholdControl.addEventListener('input', () => {
      updateControlValue();
      raster.changed();
    });

  }

  cancel(options?: any): any {
    _.forIn(this.currentLayers, (value) => {
      this.map.removeLayer(value);
    })
    this.currentLayers = {};
    _.forIn(this.currentEvent, (value, key) => {
      unByKey(value);
    })
    this.map.getView().animate({ center: [108.943904, 34.319323] }, { zoom: 5 });

  }

  private growRegion = (inputs, data) => {
    let image = inputs[0];
    let seed = data.pixel;
    let delta = parseInt(data.delta);
    if (!seed) {
      return image;
    }

    seed = seed.map(Math.round);
    let width = image.width;
    let height = image.height;
    let inputData = image.data;
    let outputData = new Uint8ClampedArray(inputData);
    let seedIdx = (seed[1] * width + seed[0]) * 4;
    let seedR = inputData[seedIdx];
    let seedG = inputData[seedIdx + 1];
    let seedB = inputData[seedIdx + 2];
    let edge = [seed];
    while (edge.length) {
      let newedge = [];
      for (let i = 0, ii = edge.length; i < ii; i++) {
        // As noted in the Raster source constructor, this function is provided
        // using the `lib` option. Other functions will NOT be visible unless
        // provided using the `lib` option.
        let next = this.next4Edges(edge[i]);
        for (let j = 0, jj = next.length; j < jj; j++) {
          let s = next[j][0];
          let t = next[j][1];
          if (s >= 0 && s < width && t >= 0 && t < height) {
            let ci = (t * width + s) * 4;
            let cr = inputData[ci];
            let cg = inputData[ci + 1];
            let cb = inputData[ci + 2];
            let ca = inputData[ci + 3];
            // if alpha is zero, carry on
            if (ca === 0) {
              continue;
            }
            if (Math.abs(seedR - cr) < delta && Math.abs(seedG - cg) < delta &&
              Math.abs(seedB - cb) < delta) {
              outputData[ci] = 255;
              outputData[ci + 1] = 0;
              outputData[ci + 2] = 0;
              outputData[ci + 3] = 255;
              newedge.push([s, t]);
            }
            // mark as visited
            inputData[ci + 3] = 0;
          }
        }
      }
      edge = newedge;
    }
    return { data: outputData, width: width, height: height };
  }

  private next4Edges = (edge) => {
    let x = edge[0];
    let y = edge[1];
    return [
      [x + 1, y],
      [x - 1, y],
      [x, y + 1],
      [x, y - 1]
    ];
  }

}
